package com.wh1200.mybatis.xmlless.model

import com.wh1200.mybatis.xmlless.model.base.BaseCriteria
import com.wh1200.mybatis.xmlless.model.component.ReplacePropertyToColumnVisitor
import com.wh1200.mybatis.xmlless.model.criteria.CriteriaParameter
import com.wh1200.mybatis.xmlless.model.visitor.ReplaceToParamVisitor
import com.wh1200.mybatis.xmlless.resolver.ColumnsResolver
import net.sf.jsqlparser.expression.BinaryExpression
import net.sf.jsqlparser.expression.Expression
import net.sf.jsqlparser.expression.ExpressionVisitorAdapter
import net.sf.jsqlparser.expression.operators.relational.Between
import net.sf.jsqlparser.expression.operators.relational.InExpression
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression
import net.sf.jsqlparser.schema.Column

fun Expression.getColumns(): List<Column> {
  val visitor = ColumnVisitor()
  this.accept(visitor)
  return visitor.getColumns()
}

/**
 *
 * @author 吴昊
 * @date 2023/12/14 22:30
 * @since v4.0.0
 * @version 1.0
 */
class ExpressionCriteria(
    val expression: Expression,
    parameters: List<CriteriaParameter>,
    methodInfo: MethodInfo,
    append: ConditionConnector,
    private var leftProcessed: Boolean = false,
    private var rightProcessed: Boolean = false
) : BaseCriteria(parameters, methodInfo, append) {

  private val properties = resolveProperties()
  private val visitor = ReplacePropertyToColumnVisitor(methodInfo)
  private val middleTables: List<TableName>

  init {
    if (!leftProcessed) {
      when (expression) {
        is BinaryExpression -> replacePropertyToColumn(expression.leftExpression)
        is Between          -> replacePropertyToColumn(expression.leftExpression)
        is IsNullExpression -> replacePropertyToColumn(expression.leftExpression)
        is InExpression     -> replacePropertyToColumn(expression.leftExpression)
        else                -> error("不支持的操作")
      }
      leftProcessed = true
    }
    if (!rightProcessed) {
      when (expression) {
        is BinaryExpression -> replaceParam(expression.rightExpression)
        is Between          -> {
          replaceParam(expression.betweenExpressionStart)
          replaceParam(expression.betweenExpressionEnd)
        }

        is InExpression,
        is IsNullExpression -> {
          // do nothing
        }

        else                -> error("不支持的操作")
      }
      rightProcessed = true
    }
    middleTables = visitor.getMiddleTables()
  }

  override fun toSqlWithoutTest(): String {
    return toSql()
  }

  override fun getTableNames(): List<String> {
    return middleTables.map { it.getAliasOrName() }
  }

  override fun getColumns(extraJoins: List<JoinInfo>): List<SelectColumn> {
    return properties.map {
      ColumnsResolver.resolveColumnByPropertyName(
          it, methodInfo, forHaving
      )
    }.flatten()
  }

  override fun getProperty(): String {
    error("不支持的操作")
  }

  override fun toSql(): String {
    return toSql(null)
  }

  override fun toSql(parameterTest: TestInfo?, ifTag: String): String {
    if (expression is IsNullExpression) {
      return "${replaceChar(expression.toString())} $append"
    }
    return wrapWithTests("${replaceChar(expression.toString())} $append", parameterTest, ifTag)
  }

  override fun clone(parameters: List<CriteriaParameter>): ExpressionCriteria {
    return ExpressionCriteria(
        expression,
        parameters,
        methodInfo,
        append,
        leftProcessed,
        rightProcessed
    )
  }

  override fun clone(append: ConditionConnector): BaseCriteria {
    return ExpressionCriteria(
        expression,
        parameters,
        methodInfo,
        append,
        leftProcessed,
        rightProcessed
    )
  }

  override fun toString(): String {
    return this.toSql()
  }

  private fun replaceChar(str: String): String {
    return str.replace(">", "&gt;").replace("<", "&lt;")
  }

  private fun resolveProperties(): List<String> {
    return when (expression) {
      is BinaryExpression -> expression.leftExpression.getColumns().map { it.toString() }
      is Between          -> expression.leftExpression.getColumns().map { it.toString() }
      is IsNullExpression -> expression.leftExpression.getColumns().map { it.toString() }
      is InExpression     -> expression.leftExpression.getColumns().map { it.toString() }
      else                -> error("不支持的操作")
    }
  }

  private fun replaceParam(exp: Expression) {
    exp.accept(ReplaceToParamVisitor(parameters, methodInfo))
  }

  private fun replacePropertyToColumn(exp: Expression) {
    exp.accept(visitor)
  }

}

class ColumnVisitor : ExpressionVisitorAdapter() {

  private val columns = mutableListOf<Column>()

  override fun visit(col: Column) {
    columns.add(col)
  }

  fun getColumns(): List<Column> {
    return columns
  }

}
